package com.shop.web.filter;

import com.alibaba.fastjson.JSON;
import com.shop.jpa.support.PageInit;
import com.shop.util.IdSignUtil;
import com.shop.web.context.WebContext;
import com.shop.web.exception.NeedLoginException;
import com.shop.web.response.BaseResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
 * 收集请求参数过滤器，加载公共参数到WebContext
 **/
@WebFilter(urlPatterns = "/*")
@Slf4j
public class HttpServletContextFilter implements Filter {

    public void init(FilterConfig filterConfig) {
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) {
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
        String contentType = httpRequest.getContentType();

        // 解决contentType=application/x-www-form-urlencoded时，将流读取并存到copy字节数组里之后，httpRequest.getParameterMap()返回空值问题
        if ((contentType != null) && (contentType.contains("application/x-www-form-urlencoded"))) {
            httpRequest.getParameterMap();
        }

        try {
            // 为了保证Steam不关闭,强制转换为BodyReaderHttpServletRequestWrapper
            httpRequest = new BodyReaderHttpServletRequestWrapper(httpRequest);

            // 加载公共请求参数上下文
            String accessToken = httpRequest.getHeader("accessToken");
            String clientType = httpRequest.getHeader("ct");
            String clientModel = httpRequest.getHeader("cm");
            String requestTimestamp = httpRequest.getHeader("ts");
            String clientVersion = httpRequest.getHeader("cv");
            String clientId = httpRequest.getHeader("ci");
            WebContext.setAccessToken(accessToken);
            WebContext.setClientType(clientType);
            WebContext.setClientModel(clientModel);
            WebContext.setRequestTimestamp(requestTimestamp);
            WebContext.setClientVersion(clientVersion);
            WebContext.setClientId(clientId);
            WebContext.setRequestIp(getIp(httpRequest));

            // 获取requestbody
            String requestBody = WebContext.requestBody(httpRequest);
            if(StringUtils.isNotBlank(requestBody)) {
                WebContext.setRequestBody(requestBody);
                // 加载分页请求参数
                if(JSON.isValidObject(requestBody)) {
                    Map<String, String> map = JSON.parseObject(requestBody, Map.class);
                    String pageNo = String.valueOf(map.get("pageNo"));
                    String pageSize = String.valueOf(map.get("pageSize"));
                    WebContext.setPageNo(PageInit.initPageNo(pageNo));
                    WebContext.setPageSize(PageInit.initPageSize(pageSize));

                    String sign = String.valueOf(map.get("sign"));
                    if(StringUtils.isNotBlank(sign)) {
                        WebContext.setSignId(IdSignUtil.checkSign(sign));
                    }
                }

            }

            chain.doFilter(httpRequest, httpResponse);
        } catch (Exception e) {
            if(e.getCause().getClass() != NeedLoginException.class
                && e.getClass() != ClientAbortException.class) {
                log.error("收集请求参数过滤器异常！" + e.getClass().toString(), e);
            }
            try {
                httpResponse.setContentType("application/json;charset=UTF-8");
                httpResponse.getWriter().write(JSON.toJSONString(BaseResponse.FAIL));
            } catch (IOException e1) {
                log.error("收集请求参数过滤器异常后返回错误信息异常！", e);
            }
        } finally {
            // 回收
            WebContext.clearThreadLocals();
        }
    }

    @Override
    public void destroy() {
    }

    public String getIp(HttpServletRequest request) {
        // 反向代理时获取真实ip
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        if("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "localhost";
        }

        if(StringUtils.isBlank(ip)) return ip;

        int douhaoIdx = ip.indexOf(",");
        if(douhaoIdx > 0) {
            ip = ip.substring(0, douhaoIdx);
        }

        return ip;
    }
}
